package net.freestu.sola.web.spider.ballerina.core;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.freestu.sola.web.global.AllLinksHashtable;
import net.freestu.sola.web.global.AllUrlQueues;
import net.freestu.sola.web.global.GlobleVar;
import net.freestu.sola.web.global.LinkHash;
import net.freestu.sola.web.global.SeedQueue;
import net.freestu.sola.web.global.ThreadPool;
import net.freestu.sola.web.global.UnLinkHash;
import net.freestu.sola.web.spider.ballerina.myUtil.URLUtil;
import net.freestu.sola.web.spider.ballerina.myUtil.URLUtilFactory;
import net.freestu.sola.web.spider.ballerina.myUtil.UrlQueue;
import net.freestu.sola.web.spider.urlfilter.ContentTypeFilter;
import net.freestu.sola.web.spider.urlfilter.IPFilter;
import net.freestu.sola.web.spider.urlfilter.LinkFilter;
import net.freestu.sola.web.spider.urlfilter.LinksForbider;
import net.freestu.sola.web.spider.urlfilter.MyLink;
import net.freestu.sola.web.spider.urlfilter.NewPostfixFilter;
import net.freestu.sola.web.spider.urlfilter.WebFilter;
import net.freestu.sola.web.util.FileManager;
import net.freestu.sola.web.util.MyLogger;
import net.freestu.sola.web.util.MyProperty;
import net.freestu.sola.web.util.TagOperator;


/**
 * 繁殖spider程序出现在这个地方。
 * @author 秋刀鱼的滋味
 */
public class UrlPicker {

	private String preUrl;// 父URL
    private String redirect;
    private MyProperty header;//网页源码上面的东西
	private File pagefile;// 源文件
	private String content;// 读取文件
	private URLUtil urlcontainer;// URL容器..这个只是该层网页中的urlcontenter。而不是整个站点的。
	// -------------------------------------------------------------
	// =URLUtilFactory.getURLUtil("HashSet");//用Set过滤重复的URL
	// 越来越能体会设计模式的强大了， 只要想改一下url容器，只需将HashSet改变一下。
	// 其他的完全可以不改变。
	// -------------------------------------------------------------
	private LinkHash table;//这个是用于整个Spider查重的链接。
	private UnLinkHash untable;//这个用于不能进行链接的地址。
	
	private String name;

	/**
	 * perUrl是父类URL
	 */
	public UrlPicker(String pagefile,String name){   
         
		this.name=name;
		urlcontainer = URLUtilFactory.getURLUtil("HashSet");// 用HashSet装URL
		AllLinksHashtable alltable = AllLinksHashtable.getInstance();// 获取全局URL的Hash表。
		
		table=alltable.get(name);
		untable=alltable.getun(name);
		
		this.pagefile = new File(pagefile);
		
		readHeader();
		readToContent();
	}

	public URLUtil getURLContainer() {

		return urlcontainer;
	}

	public String getContent() {

		return content;
	}

	
	
	/**
	 * 从全文中读取链接
	 */
	public void readLinks() {

		String regex = "[\"|'](http://)?(([\\w-]+\\.)+[\\w-]+)?(/[\\w- ./?%&=]*)?[\"|']";
		/*
		 * 对<a href=/aaa.jsp></a> 这种形式的不能提取出来。这个地方需要好好想一下。
		 */
		Pattern pt = Pattern.compile(regex);
		Matcher mt = pt.matcher(content);
         
		String seedname="";
		
		ArrayList al1=new ArrayList();//10.10修改
		while (mt.find()) {

			String str = mt.group();
			//这里提取出来的URL像"http://www.ddd.com/"
			al1.add(str);//初始链接，还没有进行处理。
		}
		
		//整个方法是public的。
		ArrayList al2=pickUrlWithHref(TagOperator.digSingleTag("a", content));
		
		ArrayList allLink;
	
	    //!!!这个地方需要对这些进行是否为空的判断！！！
		if(al2!=null){
			al2.addAll(al1);
			allLink=al2;
		}else{
			allLink=al1;
		}
			
		//str = simpleFiter(str);// 初级过滤。应该把后面的的“/“去掉
        //ArrayList all=;这里保存的是从另一种算法中提取的URL.    
		//将两个ArrayList合并起来。
       
       ThreadPool tp=ThreadPool.getInstance();
       SeedQueue sq=SeedQueue.getInstance();
       
       Iterator itoflinks=allLink.iterator();
       while(itoflinks.hasNext()){
    	   
    	   String str=simpleFiter((String)itoflinks.next());//主要是将一些地址进行规范，将相对地址转化为绝对地址。和对地址进行规范
    	   
       //#-----------繁殖程序，自动判断是否符合种子。然后启动线程。--------------------
       //----------------------------------------------------------
       //如果生成了蜘蛛的那个页面，在本蜘蛛里面就不进行下载了，不放进队列。   
    	   
    	   
       if(GlobleVar.BREED_ABL){//判断是否指定为自动繁殖       

    	   MyLink ml = new MyLink(str);
    	   LinkFilter ipf=new IPFilter(ml);
    	   str=ipf.getLinkAfterFilter();
    	   //http://202.119.112.111/index.jsp
    	   //http://202.119.111.222:8080/
    	   //http://www.cdut.edu.cn/index.jsp
    	   //http://www.sina.com/
    	   
    	     if((str!=null)&&(str.trim()!="")){//匹配站内IP地址。
    	    	 
    	    	 Pattern p = Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?");
    		     Matcher m = p.matcher(str);
    		     
    		     if(m.find()){//这里必须先find再group.
    		    	 
    			     String seednameOfIp=m.group().replace(":", "$");
    			     
    			     if((!tp.exist(seednameOfIp))&&(!sq.contains(seednameOfIp))){
    					 sq.push(seednameOfIp);
    				 }
    		     }else if(isSeed(str)){//这个匹配下来尾部没有"/"
    				 
    		    	 
				    seedname=cut(str).replaceFirst("http://", "");
				  //--------避免出现重复的Spider.
				  if((!tp.exist(seedname))&&(!sq.contains(seedname))){
					  
					 sq.push(seedname);
				}
			   }
    	     }
       }
       //----------------------------------------------------------------	
       
			if (str != null) { // 初级过滤可能返回null
				urlcontainer.addURL(str);
			}
		}
	}
	
	
	/**
	 * 将urlcontainer的文件全部输出到LinkDB下的L文件
	 */
	public void saveToLinkDB(String path, String name) {

		FileManager.buildFile(path, name); // 在LinkDB建立Ln文件
		try {
			FileWriter fw = new FileWriter(path + File.separator + name, true); // 创建一个FileWriter对象
																				// 写到磁盘(可以后面加内容)
			BufferedWriter bw = new BufferedWriter(fw); // 创建一个BufferedReader对象
			Iterator it = urlcontainer.urlIterator();
			while (it.hasNext()) {

				String str = (String) it.next();// 这里如果不兼容，直接强制转化就行了。
				bw.write(str);
				bw.newLine();
			}
			bw.flush(); // 清空缓存
			bw.close();
			fw.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 * 对URL进行比较复杂的过滤。主要是改变urlcontainer里面的链接。
	 */
	public void complexFilter(){

		// 1.从容器里面依次获取URL进行判断
		// 2.对URL进行过滤。
		// 3.对URL容器urlcontainer进行修改。
		URLUtil un = URLUtilFactory.getURLUtil("HashSet");

		Iterator it = urlcontainer.urlIterator();
		while (it.hasNext()) {
			
			String str = cut((String) it.next());
			
			//---------这个地方的untable确实应该用。，但应该不能
			if(table.contains(str)||untable.contains(str)){//如果在linkhash中有则不加入连接了。
				continue;
			}
			//----------
			
			MyLink ml = new MyLink(str);
			//如果这个地方将name的值设置成ip地址了的话就没必要在这个地方过滤IP了。
			//这里还是需要先按文件名过滤。提高速度！！
			//不过这个地方的的后缀过滤过滤掉一些常见的(.jpg .wav .mp3)等文件。自定义的文件在后面网页内容过滤器的时候过滤掉。
			
			//注意：这个地方的过滤的顺序必须设置好。！！！很关键。
			//LinkFilter ipf=new IPFilter(ml);
			LinkFilter lfb=new LinksForbider(ml);
			LinkFilter pf = new NewPostfixFilter(lfb);// 按照后缀名过滤。
			LinkFilter wf = new WebFilter(pf, name);// 按照站点过滤。!!!这个地方以后需要动态分配。
			
			String swf=wf.getLinkAfterFilter();//获取站点的的
			
			LinkFilter ctf=new ContentTypeFilter(wf);//按照报头中content-type,这个还可以过滤掉没有连接的URL.
			
			String ss = ctf.getLinkAfterFilter();
			
			
			AllLinksHashtable alltable = AllLinksHashtable.getInstance();
			UnLinkHash untable=alltable.getun(name);
			
			if (ss != null) {

				un.addURL(ss);// 加入URL容器
			}else if(swf!=null){
				untable.put(str);//放入UnLinkHash
			}
		}
		urlcontainer = un;// 改变容器。
	}

	/**
	 * 加入URL的Hash表和URL列队。
	 */
	public void addLinksToHash() {

		
		Iterator it = urlcontainer.urlIterator();
		AllUrlQueues auq=AllUrlQueues.getInstance();
		UrlQueue uq=auq.get(name);
		
		while (it.hasNext()) {

			//!!!这个地方没有考虑如果是重定向和原来的，则会重复下载相同的网页。
			//解决方案：
			//1.在ContentTyoeFilter中将文文件放入Hash表中。(但会出现一个问题，这个过滤类将会和UrlPicker形成很强的耦合)并不是好方法。
			//2.直接把程序放到此处，再对URL进行连接，判断重定向（但这会降低程序的效率）
			//两种解决方法各有优点，各有缺点，到底改选谁啊~~~还有没另外的解决方案。
			
			String str = cut((String) it.next());
			if (!table.contains(str)) {// 1如果Hash表中没有则加入URL列队
				uq.put(str);
			}
			table.put(str);// 2.加入Hash表.
		}
		untable.writeDataOut();
		table.writeDataOut();
	}
	
	private void readToContent(){
		
		try {
		       FileReader fr = new FileReader(pagefile);   //创建一个FileReader对象   从磁盘读
		       BufferedReader br = new BufferedReader(fr);    //创建一个BufferedReader对象
		      
		       
		       String tn=Thread.currentThread().getName();
		       //preUrl=GlobleVar.WEB_NAME+tn+"/";//这个相当于是ROOT文件夹，而不再是当前页面的URL。
		       
		       preUrl=header.getPro("Url");
		       redirect=header.getPro("Redirect");
		       table.put(cut(preUrl));
		       table.put(cut(redirect));
		       br.readLine();//将时间略过。
		       br.readLine();//把分数略过掠过
		       br.readLine();//将“------------”略过。
		       String line="";
		       while(br.ready()){                             //如果文件准备好，就继续读
		         line+=br.readLine();                          //读一行
		       }
		       content=line;
		       br.close();                                   //关闭流
		       fr.close();                                   //关闭流
		     }
		     catch (IOException ex) {
		       ex.printStackTrace();
		     } 
	}
		

	
	
	private String simpleFiter(String str) {
		
		if(str==null) return null;//过滤。
		
		str = str.replaceAll("\"|'|\\|", "");// 1.将| " ' 替换成没东西。
		str= str.replace("\\","");//将斜杠去掉 \"http:dddd\"
		
		if(str.replace(" ","").equals("")) return null;
		
//		第一重过滤
		if(str.startsWith("#")||str.endsWith("=")||str.toLowerCase().startsWith("javascript:")||str.toLowerCase().startsWith("mailto:")||str.toLowerCase().startsWith("tencent:")||str.toLowerCase().startsWith("ftp:")||str.toLowerCase().startsWith("ttp:")){
			//如果这个是"http://www.ii.com/id="这种肯定不行啊？
			//由于这个地方的#是锚点的意思。只是在当前页面进行跳转。所以没有收录的必要。
			
			return null;
		}
		
		
		
		int f=str.indexOf("#");
		
		if(f!=-1){//这个地方是为了将http://www.sina.com#11 这种进行排除。
			
			str=str.substring(0,f);
		}
		
		if (str.toLowerCase().startsWith("http://")){
			return cut(str);
		}else{
			if(-1!=str.indexOf("?")){
				
				return cut(constructUrl(str,redirect,true));
			}else{
				
				return cut(constructUrl(str,redirect,false));
			}
		}
	}
	
	
	/**
	 * judge whether the url matches the seedrm.
	 * @param str 网址“http://www.sina.com或者是http://111.111.111.1/
	 * @return
	 */
	private boolean isSeed(String str){//这里判断种子也必须判断是否在IP段中的IP地址。
		
		//将最后的 "/"去掉
		return cut(str).matches(GlobleVar.SEEDRM);
	  
	}
	
	
	/**
	 * 去掉Url的最后"/"
	 * @param str
	 * @return
	 */
	private String cut(String str){
		
		String ss=str;
	    if(str!=null){	
		if(str.endsWith("/")){
		int i=str.lastIndexOf("/");
		ss=str.substring(0,i);
		}
	  }
		return ss;
	}
	
	
	/**
	 * 此方法是从全文的<a xxxxx>的xxxxx中找到找到这个href=后面的链接数组。
	 * 此方法与TagOperator.digSingleTag(...)方法相利用将会取出<a ......>中的链接。
	 * @param tagcontent
	 * @return
	 */
	public ArrayList pickUrlWithHref(ArrayList tagcontent) {

		if(tagcontent==null){
			return null;
		}
		
		ArrayList result = new ArrayList();
		Iterator it;
		try {
			it = tagcontent.iterator();
		} catch (java.lang.NullPointerException e) {
			
			return null;
		}
		
		String str = "";

		L1:
		while (it.hasNext()) {

			str=(String)it.next();
			str=str.replace("HREF","href").replace("\\", "");//将转义字符去掉。测试！
			int indexOfhref = str.indexOf("href");//
			int indexOfEqu = str.indexOf("=", indexOfhref);

			if (indexOfhref == -1) {
				continue;
			}
			// 略去等号。
			int ind = indexOfEqu + 1;
			int inds;// 最后截取开始。
			int inde;// 截取结束。

			while (true) {

                 try {
					
					str.charAt(ind);
				} catch (StringIndexOutOfBoundsException e) {//为了错误处理页面
					break L1;
				}
				if (str.charAt(ind) == ' ') {// 如果是空格的话。则略去。

					ind++;
					continue;

				} else if (str.charAt(ind) == '"') {

					inds = ind + 1;
					break;

				} else if (str.charAt(ind) == '\'') {
					inds = ind + 1;
					// System.out.println("截取开始符："+str.charAt(inds));
					break;

				} else {// 如果是=http://www.baidu.com这种没有引号的形式。
					inds = ind;
					break;
				}
			}
			
			if(inds==0){
				continue;
			}
			
			if (str.charAt(inds - 1) == '"') {// 如果开始是"

				inde = str.indexOf("\"", inds);// 得到"的后续。得到截取终点。
				// System.out.println("截取终结符："+str.charAt(inde));

			} else if (str.charAt(inds - 1) == '\'') {

				inde = str.indexOf("'", inds);
				// System.out.println("截取终结符："+str.charAt(inde));

			} else {// 对应上面最后一种

				inde = str.indexOf(" ", inds);

				if (inde == -1) {// 如果是这种形式href=http://www.baidu.com

					inde = str.length();//
				}
			}
			String gain=null;
			try {
				gain = str.substring(inds, inde);
			} catch (java.lang.StringIndexOutOfBoundsException e) {
				break L1;//如果出现了这种异常就<a class="hide" href="list.php?type=9
			}
			result.add(gain);
		}
		return result;
	}
	
	/**
	 * 取出这个下载后的网页的“----”以上的内容。
	 *
	 */
	public void readHeader(){
		try {
		       FileReader fr=null;
			try {
				fr = new FileReader(pagefile);
			} catch (java.io.FileNotFoundException e) {
				e.printStackTrace();
				//ThreadPool tp=ThreadPool.getInstance();
				// tp.destory(name);
				//return; 
			}   //创建一个FileReader对象   从磁盘读
		       
		       BufferedReader br=null;
			try {
				br = new BufferedReader(fr);
			} catch (java.lang.NullPointerException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}    //创建一个BufferedReader对象
		       
		       //br.readLine();//将时间略过。
		       //br.readLine();//将“------------”略过。
		       String line="";
		       String sss="";
		       
		       while(br.ready()){                             //如果文件准备好，就继续读
		    	  
		    	 sss=br.readLine();
		    	 if(sss.equals("---------------------")) break;
		    	 line+=sss;                          //读一行
		       }
		       header=new MyProperty(line);
		       //if
		       header.map();//将信息转移到header这个对象中。
		       
		       br.close();                                   //关闭流
		       fr.close();                                   //关闭流
		     }
		     catch (IOException ex) {
		       ex.printStackTrace();
		     }
    }
	
	
	
	/**
	 * http://www.baidu.com/aaa/sss.jsp
	 * 切除这个/sss.jsp
	 * 如果切除两次，将切除成http://www.baidu.com/
	 * 测试成功
	 * @return
	 */
	public  String cutSuffix(String url){ 
		
	   url=cut(url);//切除最后的"/"	
	   int ind1=url.lastIndexOf("/");
	   int ind2=url.length();
	   String result=url.replace(url.substring(++ind1, ind2), "");
	   return result;
	}
	
	/**
	 * (这个算法是从网上摘取的)
	 * Build a URL from the link and base provided.
	 * @param link 需要转换的链接
	 * @param base 页面的url,这个地方应该是重定向后的URL，这里测试正确。
	 * @param strict 处理link 中有“?”的情况，参见
	 * http://www.ietf.org/rfc/rfc2396.txt
	 * @return 绝对 URL.
	 * @exception MalformedURLException If creating the URL fails.
	 */
	public  String constructUrl(String link, String base, boolean strict){
		
		try {
			String path;
			boolean modified;
			boolean absolute;
			int index;
			URL url; // constructed URL combining relative link and base

			// Bug #1461473 Relative links starting with ?
			if (!strict && ('?' == link.charAt(0))) { // remove query part of base if any
				if (-1 != (index = base.lastIndexOf('?')))
					base = base.substring(0, index);
				
				url = new URL(base + link);
			} else{
				url = new URL(new URL(base), link);
			}
			path = url.getFile();
			
			
			modified = false;
			absolute = link.startsWith("/");
			if (!absolute) { // we prefer to fix incorrect relative links
				// this doesn't fix them all, just the ones at the start
				while (path.startsWith("/.")) {
					if (path.startsWith("/../")) {
						path = path.substring(3);
						modified = true;
					} else if (path.startsWith("/./") || path.startsWith("/.")) {
						path = path.substring(2);
						modified = true;
					} else
						break;
				}
			}
			// fix backslashes
			while (-1 != (index = path.indexOf("/\\"))) {
				path = path.substring(0, index + 1) + path.substring(index + 2);
				modified = true;
			}
			if (modified)
				url = new URL(url, path);

			return (url.toString());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			
			MyLogger.log("有可能是协议错误了");
			
			return null;
		}
	}
	
	
	/**
	 * 规范一些URL，用于提取
	 * 例：
	 * 1.将http://www.cdut.edu.cn 这种后面应该加/,而变成文件夹的形式。
	 * 2.将以.jsp .html .htm 等等的链接确定后面没有/
	 * 
	 * 
	 * !!这个地方还有一个算法：
	 * http://www.baidu.com/a.xxx
     * http://www.baidu.com/a.x/a.jsp
     *  
     *  就是如果最后有.jsp
	 * 
	 * 
	 * 这个方法还是有一些bug，比如说这个地方的对于后缀名的判断，其实URL的后缀名是可以随便设定的。这个问题暂且不能解决。
	 * @param str
	 * @return
	 */
	public  static  String tostandUrl(String str){
		
		if(str.endsWith("/")){
			return str;
		
		}else{

			if(str.endsWith("com")||str.endsWith("org")||str.endsWith("cn")||str.endsWith("net")){
				
				str=str+"/";
				return str;
			}else{
				
				int s=str.lastIndexOf("/");
				int e=str.length();
				
				String ss=str.substring(s, e);
				
				String[] pagetype=GlobleVar.PAGE_TYPE.split(" ");
				
				boolean bz=false;
				
				for(String type:pagetype){
					
					if(ss.indexOf(type)!=-1){
						bz=true ;
						break;
					}else{
						continue;
					}
				}
				
				if(bz==false){
					
					return str+"/";
					
				}else{
					
				return str;
				}
			  
			}
			
		}
		
		
		
	}
	
	
	
	//------------------------------------------------------------------
	
	
	
	//测试tostandUrl函数
	public static void main(String[] args){
		
	    //String str=tostandUrl("http://www.baidu.com/a.xxx");
		
		//System.out.println(str);
		
		String base="http://www.cdut.net/index.htm";
		String link="favicon.ico";
		
		try {
			URL url = new URL(new URL(base), link);
			
			System.out.println(url);
			
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
	/*
	public static void main(String[] args){
		
		//String regex = "[\"|'](http://)?(([\\w-]+\\.)+[\\w-]+)?(/[\\w- ./?%&=]*)?[\"|']";
		//String ss=args[0];
		//System.out.println(ss.matches(ss));
		
		String str="\\\"http://www.baidu.com/dddd/add.jsp\\\"";
		//System.out.println(UrlPicker.cutSuffix(str));
		
		//String s= UrlPicker.cutSuffix(str);
		
		//System.out.println(UrlPicker.cutSuffix(s));
		
		System.out.println(str.replace("\\", ""));
		
	}*/
	// ----------------------------------------------------------------------------------------------------
	/*
	 * public static void main(String[] args){
	 * 
	 * AllLinksHashtable table=AllLinksHashtable.getInstance();
	 * 
	 * 
	 * UrlPicker up=new UrlPicker("./PageDB/P1");//需要获取父URL up.readLinks();//读链接
	 * up.complexFilter();//过滤 up.addLinksToHash();//加入Hash
	 * up.saveToLinkDB("./LinkDB","L1"); }
	 */
}